import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
import platform

# Set font based on operating system
if platform.system() == 'Windows':
    plt.rcParams['font.sans-serif'] = ['SimHei']  # Windows common Chinese font
elif platform.system() == 'Darwin':  # macOS
    plt.rcParams['font.sans-serif'] = ['STHeiti']
else:  # Linux or other
    plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']  # Example font, may vary based on system

# Set minus sign to display correctly
plt.rcParams['axes.unicode_minus'] = False

# Generate synthetic data with variance along specific directions
def generate_data():
    np.random.seed(42)
    mean = [2, 2]
    cov = [[3, 1], [1, 1]]  # Covariance matrix for elongated variance
    X = np.random.multivariate_normal(mean, cov, 100)
    return X

# Animate PCA projection
def animate_pca(X):
    # Fit PCA to the data
    pca = PCA(n_components=2)
    pca.fit(X)
    components = pca.components_
    explained_variance = pca.explained_variance_ratio_

    # Set up the plot
    fig, ax = plt.subplots(figsize=(8, 8))
    ax.scatter(X[:, 0], X[:, 1], alpha=0.5, label="原始数据点")
    origin = np.mean(X, axis=0)

    # Function to plot principal component arrows
    def plot_principal_components():
        ax.quiver(
            origin[0], origin[1],
            components[0, 0], components[0, 1],
            scale=3, scale_units="xy", color="r", label=f"主成分 1 ({explained_variance[0]:.2%} 方差)"
        )
        ax.quiver(
            origin[0], origin[1],
            components[1, 0], components[1, 1],
            scale=3, scale_units="xy", color="b", label=f"主成分 2 ({explained_variance[1]:.2%} 方差)"
        )

    # Initial plot setup
    ax.axhline(0, color="black", linewidth=0.5)
    ax.axvline(0, color="black", linewidth=0.5)
    ax.set_xlabel("特征 1")
    ax.set_ylabel("特征 2")
    ax.set_title("PCA 主成分分析 (2D → 1D 投影)")
    ax.legend()
    plt.pause(1)

    # Step 1: Show the principal components
    plot_principal_components()
    plt.draw()
    plt.pause(2)

    # Step 2: Animate projection of each point onto the first principal component
    projected_points = np.dot(X - origin, components[0].reshape(-1, 1)) * components[0] + origin
    for i in range(len(X)):
        # Draw line from the original point to the projected point
        ax.plot([X[i, 0], projected_points[i, 0]], [X[i, 1], projected_points[i, 1]], "gray", linestyle="--", alpha=0.5)
        # Plot the projected point on the principal component
        ax.scatter(projected_points[i, 0], projected_points[i, 1], color="orange", alpha=0.7)
        plt.draw()
        plt.pause(0.1)  # Adjust for animation speed

    plt.ioff()
    plt.show()

if __name__ == "__main__":
    # Generate data and animate PCA
    X = generate_data()
    animate_pca(X)